package org.agent.aigccore.core.service.log.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import org.agent.aigccore.core.service.log.LogService;
import org.agent.aigccore.dao.mappers.postgres.OpsLogMapper;
import org.agent.aigccore.model.dto.LogQueryDTO;
import org.agent.aigccore.model.po.OpsLogPO;
import org.agent.aigccore.model.vo.PageResultVO;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.agent.aigccore.basic.config.TimezoneConfig;

/**
 * 日志服务实现类
 * 提供日志的异步记录、查询和统计功能
 */
@Service
@ConditionalOnProperty(value = "app.logging.batch.enabled", havingValue = "true", matchIfMissing = true)
public class LogServiceImpl implements LogService {
private static final Logger log = LoggerFactory.getLogger(LogServiceImpl.class);

    private final BlockingQueue<OpsLogPO> queue = new ArrayBlockingQueue<>(10000);

    @Resource
    private OpsLogMapper opsLogMapper;

    @Resource
    private ObjectMapper objectMapper;

    @Override
    public void offer(OpsLogPO log) {
        if (log.getTs() == null) {
            log.setTs(TimezoneConfig.now());
        }
        queue.offer(log);
    }

    // 每500ms批量落库，避免频繁写入
    @Scheduled(fixedDelay = 500)
    public void flushBatch() {
        final int maxBatchSize = 128;
        final int maxRetries = 3; // 最大重试次数
        List<OpsLogPO> batch = new ArrayList<>(maxBatchSize);
        queue.drainTo(batch, maxBatchSize);
        
        if (batch.isEmpty()) {
            return;
        }
        
        int retryCount = 0;
        boolean success = false;
        
        // 重试机制
        while (retryCount < maxRetries && !success) {
            try {
                opsLogMapper.batchInsert(batch);
                success = true;
            } catch (Exception ex) {
                retryCount++;
                
                // 记录错误但不抛出，避免影响后续日志处理
                try {
                    // 这里使用标准输出，实际环境中可以使用其他日志框架
                    System.err.println("Failed to batch insert logs, retry count: " + retryCount + ", error: " + ex.getMessage());
                    
                    // 随着重试次数增加，增加等待时间，避免短时间内频繁重试
                    if (retryCount < maxRetries) {
                        Thread.sleep(100 * retryCount);
                    }
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        
        // 如果多次重试仍失败，则记录到系统标准错误流
        if (!success) {
            System.err.println("Failed to insert logs after " + maxRetries + " retries, logs count: " + batch.size());
            // 在生产环境中，可以考虑将失败的日志写入文件或其他可靠存储
        }
        
        batch.clear();
 }

    @Override
    public void logOperation(String module, String action, String userId, String ip, String method, String path, boolean success, String message, Object extra) {
        OpsLogPO log = new OpsLogPO();
        log.setLogType("OPERATION");
        log.setLevel("INFO");
        log.setModule(module);
        log.setAction(action);
        log.setUserId(userId);
        log.setIp(ip); // Set IP
        log.setMethod(method); // Set Method
        log.setPath(path); // Set Path
        log.setSuccess(success);
        log.setMessage(message);
        
        // 使用新添加的方法设置extra字段，确保正确处理JSON数据
        if (extra != null) {
            try {
                if (extra instanceof Map) {
                    log.setExtraMap((Map<String, Object>) extra);
                } else {
                    log.setExtra(objectMapper.writeValueAsString(extra));
                }
            } catch (Exception e) {
                try {
                    log.setExtra(extra.toString());
                } catch (Exception ex) {
                    log.setExtra("{\"error\":\"Failed to serialize extra data\"}");
                }
            }
        }
        
        offer(log);
    }

    @Override
    public void logSystem(String level, String module, String message, Throwable throwable) {
        OpsLogPO log = new OpsLogPO();
        log.setLogType("SYSTEM");
        log.setLevel(level);
        log.setModule(module);
        log.setMessage(message);
        log.setSuccess(false);
        if (throwable != null) {
            log.setErrorCode(throwable.getClass().getSimpleName());
            StringBuilder stackBuilder = new StringBuilder();
            for (StackTraceElement element : throwable.getStackTrace()) {
                stackBuilder.append(element.toString()).append("\n");
            }
            log.setStack(stackBuilder.toString());
        }
        offer(log);
    }

    @Override
    public void logSchedule(String schedulerId, String robotId, String taskId, String taskType, boolean success, String message, Object extra) {
        OpsLogPO log = new OpsLogPO();
        log.setLogType("SCHEDULE");
        log.setLevel(success ? "INFO" : "ERROR");
        log.setModule("SCHEDULER");
        log.setSchedulerId(schedulerId);
        log.setRobotId(robotId);
        log.setTaskId(taskId);
        log.setTaskType(taskType);
        log.setSuccess(success);
        log.setMessage(message);
        
        // 使用新添加的方法设置extra字段，确保正确处理JSON数据
        if (extra != null) {
            try {
                if (extra instanceof Map) {
                    log.setExtraMap((Map<String, Object>) extra);
                } else {
                    log.setExtra(objectMapper.writeValueAsString(extra));
                }
            } catch (Exception e) {
                try {
                    log.setExtra(extra.toString());
                } catch (Exception ex) {
                    log.setExtra("{\"error\":\"Failed to serialize extra data\"}");
                }
            }
        }
        
        offer(log);
    }

    @Override
    public void logDevice(String deviceId, String deviceType, String action, boolean success, String message, Object deviceData) {
        OpsLogPO log = new OpsLogPO();
        log.setLogType("DEVICE");
        log.setLevel(success ? "INFO" : "ERROR");
        log.setModule("DEVICE");
        log.setAction(action);
        log.setDeviceId(deviceId);
        log.setDeviceType(deviceType);
        log.setSuccess(success);
        log.setMessage(message);
        
        // 使用新添加的方法设置deviceData字段，确保正确处理JSON数据
        if (deviceData != null) {
            try {
                if (deviceData instanceof Map) {
                    log.setDeviceDataMap((Map<String, Object>) deviceData);
                } else {
                    log.setDeviceData(objectMapper.writeValueAsString(deviceData));
                }
            } catch (Exception e) {
                try {
                    log.setDeviceData(deviceData.toString());
                } catch (Exception ex) {
                    log.setDeviceData("{\"error\":\"Failed to serialize device data\"}");
                }
            }
        }
        
        offer(log);
    }

    @Override
    public PageResultVO<OpsLogPO> queryLogs(LogQueryDTO queryDTO) {
        // 构建查询条件
        if (queryDTO == null) {
            throw new IllegalArgumentException("查询参数不能为空");
        }
        
        // 验证分页参数
        if (queryDTO.getPageNum() == null || queryDTO.getPageNum() <= 0) {
            queryDTO.setPageNum(1);
        }
        if (queryDTO.getPageSize() == null || queryDTO.getPageSize() <= 0) {
            queryDTO.setPageSize(10);
        }
        
        // 验证时间范围
        if (queryDTO.getStartTime() != null && queryDTO.getEndTime() != null && 
            queryDTO.getStartTime().isAfter(queryDTO.getEndTime())) {
            throw new IllegalArgumentException("开始时间不能晚于结束时间");
        }
        
        // 验证JSON字段查询参数 - 确保字段名和字段值成对出现
        if ((queryDTO.getExtraField() != null && queryDTO.getExtraValue() == null) || 
            (queryDTO.getExtraField() == null && queryDTO.getExtraValue() != null)) {
            throw new IllegalArgumentException("extra字段查询参数不完整，需要同时提供字段名和字段值");
        }
        
if ((queryDTO.getDeviceDataField() != null && queryDTO.getDeviceDataValue() == null) || 
            (queryDTO.getDeviceDataField() == null && queryDTO.getDeviceDataValue() != null)) {
            throw new IllegalArgumentException("deviceData字段查询参数不完整，需要同时提供字段名和字段值");
        }
        
        // 查询日志列表
        List<OpsLogPO> logs = opsLogMapper.selectByPage(
            queryDTO.getLogType(),
            queryDTO.getLevel(),
            queryDTO.getApp(),
            queryDTO.getModule(),
            queryDTO.getAction(),
            queryDTO.getUserId(),
            queryDTO.getSuccess(),
            queryDTO.getStartTime(),
            queryDTO.getEndTime(),
            queryDTO.getKeyword(),
            queryDTO.getDeviceId(),
            queryDTO.getRobotId(),
            queryDTO.getTaskId(),
            queryDTO.getExtraField(),
            queryDTO.getExtraValue(),
            queryDTO.getDeviceDataField(),
            queryDTO.getDeviceDataValue(),
            queryDTO.getPageSize(),
            queryDTO.getOffset()
        );

        // 查询总记录数
        Long total = opsLogMapper.countByCondition(
            queryDTO.getLogType(),
            queryDTO.getLevel(),
            queryDTO.getApp(),
            queryDTO.getModule(),
            queryDTO.getAction(),
            queryDTO.getUserId(),
            queryDTO.getSuccess(),
            queryDTO.getStartTime(),
            queryDTO.getEndTime(),
            queryDTO.getKeyword(),
            queryDTO.getDeviceId(),
            queryDTO.getRobotId(),
            queryDTO.getTaskId(),
            queryDTO.getExtraField(),
            queryDTO.getExtraValue(),
            queryDTO.getDeviceDataField(),
            queryDTO.getDeviceDataValue()
        );
        
        // 处理返回的JSONB字段，转换为可读格式
        if (logs != null && !logs.isEmpty()) {
            for (OpsLogPO logPO : logs) {
                try {
                    // 确保extra和deviceData字段正确解析
                    if (logPO.getExtra() != null) {
                        logPO.getExtraMap(); // 触发JSON解析
                    }
                    if (logPO.getDeviceData() != null) {
                        logPO.getDeviceDataMap(); // 触发JSON解析
                    }
                } catch (Exception e) {
                    // 记录解析错误但不影响查询结果
                    log.warn("解析日志JSON字段失败: {}", e.getMessage());
                }
            }
        }

        return new PageResultVO<>(total, queryDTO.getPageNum(), queryDTO.getPageSize(), logs);
    }

    @Override
    public OpsLogPO getLogById(Long id) {
        return opsLogMapper.selectById(id);
    }

    @Override
    public List<OpsLogPO> getLogsByRequestId(String requestId) {
        return opsLogMapper.selectByRequestId(requestId);
    }

    @Override
    public List<Map<String, Object>> getLogStats(OffsetDateTime startTime, OffsetDateTime endTime) {
        return opsLogMapper.aggregateStats(startTime, endTime);
    }

    @Override
    public List<Map<String, Object>> getLogTrend(OffsetDateTime startTime, OffsetDateTime endTime, String interval) {
        return opsLogMapper.trendStats(startTime, endTime, interval);
    }
}



